package gs

import (
	"fmt"
	"runtime"
	"strings"
)

const (
	ANSI_LINE_UP        = "A"
	ANSI_LINE_DOWN      = "B"
	ANSI_LINE_NEXT_LINE = "E"
	ANSI_LINE_PRE_LINE  = "F"

	ANSI_CURSOR_MOVE        = "H"
	ANSI_CURSOR_SAVE        = "s"
	ANSI_CURSOR_SAVE_DARWIN = "7"

	ANSI_CURSOR_RESTORE        = "u"
	ANSI_CURSOR_RESTORE_DARWIN = "8"

	ANSI_COLUMN_MOVE  = "G"
	ANSI_COLUMN_RIGHT = "C"
	ANSI_COLUMN_LEFT  = "D"

	ANSI_ERASE_ALL_TOEND  = "0J"
	ANSI_ERASE_ALL_BEFORE = "1J"
	ANSI_ERASE_ALL_SCREEN = "2J"

	ANSI_THIS_LINE_ERASE_TOEND  = "0K"
	ANSI_THIS_LINE_ERASE_BEFORE = "1K"
	ANSI_THIS_LINE_ERASE_ALL    = "2K"

	ANSI_STYLE = "m"

	ANSI_STYLE_RESET              = 0
	ANSI_STYLE_BOLD               = 1
	ANSI_STYLE_DIM_FAINT          = 2
	ANSI_STYLE_ITALIC             = 3
	ANSI_STYLE_UNDERLINE          = 4
	ANSI_STYLE_BLINK              = 5
	ANSI_STYLE_REVERSE            = 7
	ANSI_CURSOR_HIDDEN_OR_DISPLAY = 8
	ANSI_STYLE_STRIKELINE         = 9

	FgBlack    = 30
	BGBlack    = 40
	FgRed      = 31
	BgRed      = 41
	FgGreen    = 32
	BgGreen    = 42
	FgYellow   = 33
	FgBgYellow = 43
	FgBlue     = 34
	BgBlue     = 44
	FgMagenta  = 35
	BgMagenta  = 45
	FgCyan     = 36
	BgCyan     = 46
	FgWhite    = 37
	BgWhite    = 47
	FgDefault  = 39
	BgDefault  = 49
)

func (self Str) ANSI(ANSI_FUNC_C string, ANSI_ARGS ...int) Str {
	args := Str("")
	if ANSI_ARGS != nil {
		// join[int](",", ANSI_ARGS)
		es := []string{}
		for _, code := range ANSI_ARGS {
			es = append(es, fmt.Sprint(code))
		}
		args = Str(strings.Join(es, ";"))
	}
	// print(args,)
	return Str(fmt.Sprintf("\x1b[%s%s", args, ANSI_FUNC_C))
}

func (self Str) ANSICursor(line, column int) Str {
	return self + self.ANSI(ANSI_CURSOR_MOVE, line, column)
}

func (self Str) ANSIPreLine() Str {
	return self + self.ANSI(ANSI_LINE_PRE_LINE)
}

func (self Str) ANSIEraseALL() Str {
	return self + self.ANSI(ANSI_ERASE_ALL_SCREEN)
}
func (self Str) ANSIEraseALLType2() Str {
	return self + self.ANSI("H")
}

func (self Str) ANSIClearThisLine() Str {
	return self + self.ANSI(ANSI_THIS_LINE_ERASE_TOEND)
}

func (self Str) ANSIEraseToEND() Str {
	return self + self.ANSI(ANSI_ERASE_ALL_TOEND)
}

func Sprintf(tmp string, obj ...any) Str {
	return Str(fmt.Sprintf(tmp, obj...))
}

func (self Str) ANSISave() Str {
	if runtime.GOOS == "darwin" {
		return self + Sprintf("\x1b%s", ANSI_CURSOR_SAVE_DARWIN)
	}

	return self + self.ANSI(ANSI_CURSOR_SAVE)
}

func (self Str) ANSIRestore() Str {
	if runtime.GOOS == "darwin" {
		return self + Sprintf("\x1b%s", ANSI_CURSOR_RESTORE_DARWIN)
	}
	return self + self.ANSI(ANSI_CURSOR_RESTORE)
}

func (self Str) ANSIHideOrDisCursor() Str {
	return self + self.ANSI(ANSI_STYLE, ANSI_CURSOR_HIDDEN_OR_DISPLAY)
}

func (self Str) ANSIHideCursor() Str {
	return self + self.ANSI("25l")
}
func (self Str) ANSIShowCursor() Str {
	return self + self.ANSI("25h")
}

func (self Str) ansi_end() Str {
	if self.EndsWith("\x1b[0m") {
		return self
	} else {
		return self + "\x1b[0m"
	}
}
func (self Str) ANSISelected() Str {

	return self.ANSI(ANSI_STYLE, ANSI_STYLE_REVERSE) + self.ansi_end()
}

func (self Str) ANSIStyle(ansiColorCode ...int) Str {
	return self.ANSI(ANSI_STYLE, ansiColorCode...) + self.ansi_end()
}

func (self Str) AsCharacters() List[byte] {
	o := List[byte]{}
	for _, b := range []byte(self) {
		o = append(o, b)
	}
	return o
}

func (self Str) ANSIBold() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_BOLD) + self.ansi_end()
}

// P prepend in start
func (self Str) P(a any) Str {
	switch a.(type) {
	case string:
		builder := strings.Builder{}
		builder.WriteString(a.(string))
		builder.WriteString(self.String())
		return Str(builder.String())
	case Str:
		builder := strings.Builder{}
		builder.WriteString(a.(Str).String())
		builder.WriteString(self.String())
		return Str(builder.String())
	default:
		builder := strings.Builder{}
		builder.WriteString(fmt.Sprint(a))
		builder.WriteString(self.String())
		return Str(builder.String())
	}
}

func (self Str) ANSIStrike() Str {
	if runtime.GOOS == "darwin" {
		return self.AsCharacters().Join(string('\u0336')).P("\u0336")
	}
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_STRIKELINE) + self.ansi_end()
}

func (self Str) ANSIBlink() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_BLINK) + self.ansi_end()
}

func (self Str) ANSIRed() Str {
	return self.ANSI(ANSI_STYLE, FgRed) + self.ansi_end()
}

func (self Str) ANSIMagenta() Str {
	return self.ANSI(ANSI_STYLE, FgMagenta) + self.ansi_end()
}

func (self Str) ANSIItalic() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_ITALIC) + self.ansi_end()
}

func (self Str) ANSICyan() Str {
	return self.ANSI(ANSI_STYLE, FgCyan) + self.ansi_end()
}

func (self Str) ANSIGreen() Str {
	return self.ANSI(ANSI_STYLE, FgGreen) + self.ansi_end()
}

func (self Str) ANSIYellow() Str {
	return self.ANSI(ANSI_STYLE, FgYellow) + self.ansi_end()
}

func (self Str) ANSIBlue() Str {
	return self.ANSI(ANSI_STYLE, FgBlue) + self.ansi_end()
}

func (self Str) ANSIUnderline() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_UNDERLINE) + self.ansi_end()
}

func (self Str) ANSIDimFaint() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_DIM_FAINT) + self.ansi_end()
}

func (self Str) ANSIReset() Str {
	return self.ANSI(ANSI_STYLE, ANSI_STYLE_RESET) + self.ansi_end()
}
